package com.rybbaby.tss.core.sql.metadata;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.hibernate.HibernateException;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.relational.QualifiedTableName;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.env.internal.QualifiedObjectNameFormatterStandardImpl;
import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.mapping.Table;
import org.jboss.logging.Logger;

public class DatabaseMetadata {
	
	private static final CoreMessageLogger LOG = (CoreMessageLogger)Logger.getMessageLogger(CoreMessageLogger.class , DatabaseMetaData.class.getName());
	private Map<Object , TableMetadata> tables;
	private Set<String> sequences;
	private boolean extras;
	private Dialect dialect;
	private DatabaseMetaData meta;
	private static final String [] TYPES = {"TABLE" , "VIEW" , "SYSTEM TABLE"};
	
	public DatabaseMetadata (Connection connection , Dialect dialect ) throws SQLException{
		this(connection , dialect , true);
	}
	
	public DatabaseMetadata (Connection connection , Dialect dialect , boolean extras ) throws SQLException{
		this.tables = new HashMap<Object , TableMetadata>();
		this.sequences = new HashSet<String>();
		this.meta = connection.getMetaData();
		this.extras = extras;
		this.dialect = dialect;
		initSequences(connection);
	}
	
	public TableMetadata getTableMetadata(String name , String schema , String catalog , boolean isQuoted) throws HibernateException {
		Object identifier = identifier(catalog , schema , name);
		TableMetadata table = (TableMetadata)this.tables.get(identifier);
		if(table != null) {
			return table;
		}
		
		try {
			ResultSet rs = null;
			try {
				if((isQuoted) && (this.meta.storesMixedCaseQuotedIdentifiers()))
					rs = this.meta.getTables(catalog , schema , name , TYPES);
				else if(((isQuoted) && (this.meta.storesUpperCaseQuotedIdentifiers())) || ((!(isQuoted)) && (this.meta.storesUpperCaseIdentifiers()))) {
					rs = this.meta.getTables(StringUtils.upperCase(catalog) , StringUtils.upperCase(schema) , StringUtils.upperCase(name) , TYPES);
				} else if(((isQuoted) && (this.meta.storesLowerCaseQuotedIdentifiers())) || ((!(isQuoted)) && (this.meta.storesLowerCaseIdentifiers()))) {
					rs = this.meta.getTables(StringUtils.lowerCase(catalog) , StringUtils.lowerCase(schema) , StringUtils.lowerCase(name) , TYPES);
				} else {
					rs = this.meta.getTables(catalog , schema , name , TYPES);
				}
				String tableName = null;
				while (rs.next()) {
					tableName = rs.getString("TABLE_NAME");
					if(name.equalsIgnoreCase(tableName)) {
						table = new TableMetadata(rs , this.meta , this.extras);
						this.tables.put(identifier , table);
						TableMetadata localTableMetadata1 = table;
						return localTableMetadata1;
					}
				}
				LOG.tableNotFound(name);
				return null;
			} finally {
				if(rs != null) {
					rs.close();
				}
			}
		} catch (SQLException sqlException) {
			throw new SqlExceptionHelper(true).convert(sqlException , "could not get table metadata: " + name);
		}
	}
	
	private Object identifier(String catalog , String schema , String name) {
		QualifiedObjectNameFormatterStandardImpl d = new QualifiedObjectNameFormatterStandardImpl(NameQualifierSupport.CATALOG);
		QualifiedTableName qualifiedName = new QualifiedTableName(Identifier.toIdentifier(catalog) , Identifier.toIdentifier(schema) , Identifier.toIdentifier(name));
		return d.format(qualifiedName , this.dialect);
	}
	
	private void initSequences(Connection connection) throws SQLException {
		if(dialect.supportsSequences()) {
			String sql = this.dialect.getQuerySequencesString();
			if(sql == null) {
				return;
			}
			Statement statement = null;
			ResultSet rs = null;
			try {
				statement = connection.createStatement();
				rs = statement.executeQuery(sql);
				while (rs.next()) {
					this.sequences.add(rs.getString(1).toLowerCase().trim());
				}
			} finally {
				if(rs != null) {
					rs.close();
				}
				if(statement != null) {
					statement.close();
				}
			}
		}
	}
	
	public boolean isSequence(Object key) {
		if(key instanceof String) {
			String [] strings = StringUtils.split("." , (String)key);
			return this.sequences.contains(strings[(strings.length - 1)].toLowerCase());
		}
		return false;
	}
	
	public boolean isTable(Object key) throws HibernateException {
		if(key instanceof String) {
			Table tbl = new Table((String)key);
			if(getTableMetadata(tbl.getName() , tbl.getSchema() , tbl.getCatalog() , tbl.isQuoted()) != null) {
				return true;
			}
			String [] strings = StringUtils.split("." , (String)key);
			if(strings.length == 3) {
				tbl = new Table(strings[2]);
				tbl.setCatalog(strings[0]);
				tbl.setSchema(strings[1]);
				return(getTableMetadata(tbl.getName() , tbl.getSchema() , tbl.getCatalog() , tbl.isQuoted()) != null);
			}
			if(strings.length == 2) {
				tbl = new Table(strings[1]);
				tbl.setSchema(strings[0]);
				return(getTableMetadata(tbl.getName() , tbl.getSchema() , tbl.getCatalog() , tbl.isQuoted()) != null);
			}
		}
		return false;
	}
	
	public String toString() {
		return "DatabaseMetadata" + this.tables.keySet().toString() + this.sequences.toString();
	}
}
